/**
 * This package provide the MVC model for this game.
 */
package vn.com.fsoft.oopai.fsi.mvc;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import vn.com.fsoft.oopai.fsi.common.Common;
import vn.com.fsoft.oopai.fsi.common.Level;
import vn.com.fsoft.oopai.fsi.game.GameState;
import vn.com.fsoft.oopai.fsi.rule.FifthLevelRules;
import vn.com.fsoft.oopai.fsi.rule.FirstLevelRules;
import vn.com.fsoft.oopai.fsi.rule.FourthLevelRules;
import vn.com.fsoft.oopai.fsi.rule.Rules;
import vn.com.fsoft.oopai.fsi.rule.SecondLevelRules;
import vn.com.fsoft.oopai.fsi.rule.ThirdLevelRules;

/**
 * @author OOPAI Group.
 */

public class Controller {
 /**
  * the model contains the information game such as rule and game state.
  */
	private Model model;
	private View view;

	/**
	 * Controller class control the interaction between the view of game and
	 * game state in the model.
	 * @param model contains rule and state of game
	 * @param view draw the view of game
	 */
	public Controller(Model model, View view) {
		this.model = model;
		this.view = view;
		initiateView();
		for (int i = 0; i < Common.NUMBER_OF_LEVEL; i++) {
			view.addLevelButtonActionListener(
				new LevelMouseAdapter(Level.values()[i]));
		}
		view.addConfirmButtonActionListener(
				new confirmButtonMouseAdapter());
	}

	/**
	 * initiate view of game.
	 */
	public final void initiateView() {
		int tableSize = this.model.getGameState().getTableSize();
		for (int i = 0; i < tableSize; i++) {
			for (int j = 0; j < tableSize; j++) {
				view.addOppaiMouseActionListener(
						new OopaiMouseAdapter(i, j));
			}
		}
		if (this.model.getRules().isFlagRemovable()) {
			this.view.addConfirmButton();
			this.view.addConfirmButtonActionListener(
					new confirmButtonMouseAdapter());
		}
	}

	/**
	 * reset all game state and view of the game.
	 */
	public final void resetGame() {
		model.getGameState().initGameState();
		view.resetView();
	}

	/**
	 * set all of Tool-Breaker be viable. this method is used just for test
	 */
	public final void setAllOfToolBreakerVisible() {
		GameState gameState = model.getGameState();
		int tableSize = gameState.getTableSize();
		for (int i = 0; i < tableSize; i++) {
			for (int j = 0; j < tableSize; j++) {
				if (gameState.isToolBreaker(gameState.getStatus(i, j))) {
					view.setToolBreakerVisible(i, j);
				}
			}
		}
	}

	/**
	 * notify lose on the screen.
	 */
	public final void notifyLose() {
		setAllOfToolBreakerVisible();
		view.notifyLose();
		resetGame();
	}
/**
 * Process when the event like left click
 * occur.
 * @author OOPAI Group
 *
 */
	class OopaiMouseAdapter extends MouseAdapter {
		private int x;
		private int y;

		public OopaiMouseAdapter(int x, int y) {
			super();
			this.x = x;
			this.y = y;
		}

		public void setX(int x) {
			this.x = x;
		}

		public void setY(int y) {
			this.y = y;
		}

		public void setCoordinate(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public int getX() {
			return this.x;
		}

		public int getY() {
			return this.y;
		}

		@Override
		public void mouseClicked(MouseEvent e) {
			if (!model.getRules().isAbleToFlag()) {
				leftClickProcessing();
			} else {
				if (e.getButton() == MouseEvent.BUTTON1) {
					leftClickProcessing();
				} else if (e.getButton() == MouseEvent.BUTTON3) {
					rightClickProcessing();
				}
			}
		}

		private void rightClickProcessing() {
			Rules rules = model.getRules();
			GameState gameState = model.getGameState();
			if (rules.isFlagRemovable()
					&& gameState.isFalgged(this.x, this.y)) {
				gameState.increaseRemainingFlag();
				gameState.removeFlag(this.x, this.y);
				view.removeFlag(this.x, this.y, gameState.getRemainingFlag());
			} else if (gameState.getRemainingFlag() > 0) {
				if (rules.isNumFlagAbleToAdd(gameState, this.x, this.y)) {
					gameState.addNumFlag(this.x, this.y);
					gameState.decreaseRemainingFlag();
				}
				view.setFlag(this.x, this.y, gameState.getRemainingFlag());
				if (!rules.isFlagRemovable()){
					if (rules.checkWin(gameState)) {
						view.notifyWin(gameState.getScore());
						resetGame();
					} else if (!rules.isAbleToMultipleFlag()
							&& gameState.getRemainingFlag() == 0) {
						notifyLose();

					}
				}
					
			}
		}

		private void leftClickProcessing() {
			Rules rules = model.getRules();
			GameState gameState = model.getGameState();
			if (!gameState.isToolBreaker(gameState.getStatus(this.x, this.y))) {
				if (rules.isNumClickAbleToAdd(gameState, this.x, this.y)) {
					gameState.addNumClick(this.x, this.y);
					view.setAfterClicked(this.x, this.y,
						gameState.getStatus(this.x, this.y));
				}
				if (rules.checkWin(gameState)) {
					view.notifyWin(gameState.getScore());
					resetGame();
				}
			} else {
				if (!gameState.isFalgged(this.x, this.y)) {
					notifyLose();
				}
			}
		}
	}

	class confirmButtonMouseAdapter extends MouseAdapter {

		@Override
		public void mouseClicked(MouseEvent e) {
			if (model.getRules().checkWin(model.getGameState())) {
				view.notifyWin(model.getGameState().getScore());
				resetGame();
			} else {
				notifyLose();
			}
		}
	}

	class LevelMouseAdapter extends MouseAdapter {

		private Level gameLevel;

		public Level getGameLevel() {
			return gameLevel;
		}

		public void setGameLevel(Level gameLevel) {
			this.gameLevel = gameLevel;
		}

		public LevelMouseAdapter(Level gameLevel) {
			this.gameLevel = gameLevel;
		}

		@Override
		public void mouseClicked(MouseEvent e) {

			switch (this.gameLevel) {
			case LEVEL1:
				model.getGameState().initGameState();
				model = new Model(new FirstLevelRules(),
						model.getGameState());
				break;
			case LEVEL2:
				model.getGameState().initGameState();
				model = new Model(new SecondLevelRules(),
						model.getGameState());
				break;
			case LEVEL3:
				model.getGameState().initGameState();
				model = new Model(new ThirdLevelRules(),
						model.getGameState());
				break;
			case LEVEL4:
				model.getGameState().initGameState();
				model = new Model(new FourthLevelRules(),
						model.getGameState());
				break;
			case LEVEL5:
				model.getGameState().initGameState();
				model = new Model(new FifthLevelRules(),
						model.getGameState());
				break;
			default:
				break;

			}
			
			view.resetView();
			if (!model.getRules().isAbleToFlag()) {
				view.destroyNumRemainingFlagLabel();
			} else {
				view.addNumRemainingFlagLabel();
			}

			if (model.getRules().isFlagRemovable()) {
				view.addConfirmButton();
			} else {
				view.destroyConfirmButton();
			}
		}
	}
}
